package yxy.apple.util.scheduler;

import yxy.apple.util.IDGenerator;
import yxy.apple.util.SortedQueue;

public class SingleThreadScheduler<S extends Schedulable> extends Scheduler<S> implements Runnable {

	public final String UUID = IDGenerator.nextUUID();

	static interface Listener {
		public void onStart(SingleThreadScheduler<?> scheduler);

		public void onStopped(SingleThreadScheduler<?> scheduler);
	}

	private class Task implements Comparable<Task>, ScheduledFuture {
		private S schedulable = null;
		private long expiration = 0;
		private boolean enabled = false;
		private long since = 0;

		private Task(S schedulable, long expiration) {
			this.schedulable = schedulable;
			this.since = System.currentTimeMillis();
			this.expiration = expiration;
		}

		private void run() {
			if (this.enabled) {
				this.schedulable.trigger();
			}
		}

		@Override
		public void cancel() {
			synchronized (SingleThreadScheduler.this.locker) {
				if (this.enabled) {
					this.enabled = false;
					this.schedulable.cancelled();
					SingleThreadScheduler.this.unlock();
				}
			}
		}

		@Override
		public void renew(int timeout) {
			synchronized (SingleThreadScheduler.this.locker) {
				this.since = System.currentTimeMillis();
				this.expiration = timeout + since;
				SingleThreadScheduler.this.queue.sort();
				SingleThreadScheduler.this.unlock();
			}
		}

		@Override
		public int compareTo(Task task) {
			return Long.compare(this.expiration, task.expiration);
		}

		@Override
		public long since() {
			return this.since;
		}
	}

	private SortedQueue<Task> queue = new SortedQueue<>();
	private Object locker = new Object();
	private boolean running = false;

	Listener listener = null;

	@Override
	public ScheduledFuture schedule(S schedulable, int delay) {
		synchronized (this.locker) {
			long expiration = System.currentTimeMillis() + delay;
			Task task = new Task(schedulable, expiration);
			task.enabled = true;
			this.queue.put(task);
			this.queue.sort();
			if (!this.running) {
				this.running = true;
				new Thread(this, "SchedulerThread").start();
			} else if (task == this.queue.peek()) {
				// 如果新加的任务排在了第一个，那么把线程唤醒一次
				this.unlock();
			}
			return task;
		}
	}

	@Override
	public void clear() {
		synchronized (this.locker) {
			this.queue.clear();
			this.unlock();
		}
	}

	@Override
	public void run() {
		if (this.listener != null) {
			this.listener.onStart(this);
		}
		while (this.running && !Thread.interrupted()) {
			synchronized (this.locker) {
				Task peek = this.queue.peek();
				if (peek != null) {
					if (peek.enabled) {
						long now = System.currentTimeMillis();
						if (peek.expiration <= now) {
							Task task = this.queue.poll();
							task.run();
						} else {
							this.lock(peek.expiration - now);
						}
					} else {
						// 如果首元素不可用，则移除
						this.queue.poll();
					}
				} else {
					// 如果无可用元素则停转
					this.running = false;
				}
			}
		}
		if (this.listener != null) {
			this.listener.onStopped(this);
		}
	}

	@Override
	public int scheduled() {
		return this.queue.size();
	}

	private void unlock() {
		this.locker.notify();
	}

	private void lock(long timeout) {
		try {
			this.locker.wait(timeout);
		} catch (Exception e) {
		}
	}

	public boolean running() {
		return this.running;
	}
}
